package chat;

import groupManagement.ChatGroup;
import groupManagement.User;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

import javax.jms.JMSException;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.rowset.spi.SyncResolver;

import topicHandlers.Translate;




import Logging.Monitor;

public class LingvoChat {

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
	// for the communication with rest of the groups
	static Context jndiContext = null;
	static TopicConnectionFactory topicConnectionFactory = null;
	static TopicConnection topicConnection = null;
	static TopicSession topicSession = null;
	MessageListener controlListener;
	
	// Groups info
	private Map<String, ChatGroup> groups=new HashMap<String, ChatGroup>();
	
	// user specific info
	private final String userName;
	private final String lang;
	private final static LinkedList<String> 
				allowedLang = 
				new LinkedList<String>(
						Arrays.asList(new String[]{"ENGLISH","SPANISH","FRENCH","GERMAN"})
					);
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
	public LingvoChat(String username,String lang) {
		
		this.userName = username;
		this.lang = lang;
		
	}
	
	private void initMessaging() {
		
		try {
			jndiContext = new InitialContext();
			topicConnectionFactory = (TopicConnectionFactory) 
									jndiContext.lookup("TopicConnectionFactory");
			
			topicConnection = topicConnectionFactory.createTopicConnection();
			topicSession = topicConnection.createTopicSession(false, 
															  Session.AUTO_ACKNOWLEDGE);
			
		} catch (NamingException e) {
			System.out.println("Could not connect to JMS");
			quit();
		} catch (JMSException e) {
			System.out.println("Could not connect to JMS");
			quit();
		}
	}
	
	static public TopicSubscriber initListner(String topicName,MessageListener topicListener) {
		
		try {
			Topic topic = (Topic) jndiContext.lookup(topicName);
			TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic);
			topicSubscriber.setMessageListener(topicListener); 
			topicConnection.start();	
			return topicSubscriber;
		} catch (NamingException e) {
			System.out.println("Could not connect to Topic");
		} catch (JMSException e) {
			System.out.println("Could not connect to Topic");
		} 
		return null;
	}
	
	public static void publishMsg(String topicName,String message) {
		
		try {
			Topic topic = (Topic) jndiContext.lookup(topicName);
			TopicPublisher topicPublisher = topicSession.createPublisher(topic);
			TextMessage textMessage = topicSession.createTextMessage();
			textMessage = topicSession.createTextMessage(message);
			topicPublisher.publish(textMessage);
			topicPublisher.close();
		} catch (NamingException e) {
			System.out.println("Couldnt publish msg on topic, for more details - " + e.getMessage());
		} catch (JMSException e) {
			System.out.println("Couldnt publish msg on topic, for more details - " + e.getMessage());
		}

	}
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
	private void join(String group) {
		
		Monitor.write(null, "Handling a join request for group:"+group);
		if( !groups.containsKey(group)){
			synchronized (groups) {
				groups.put(group, new ChatGroup(group, userName, lang));
			}
			try {
				Thread.sleep(10000); 
			} catch (InterruptedException ignore) {}
			
			Monitor.write(this, "Added new group");			
			// printing the participants of the group
			groupStatus(group);
		}
		else {
			System.out.println("Already in group " + group.substring(14));
		}
		
	}

	private void leave(String group) {
		
		Monitor.write(null, "Handling a leave request for  group:"+group);
		synchronized (groups) {
			if(!groups.containsKey(group)){
				System.out.println("Leave action failed - user not in group");
			}
			else {
				groups.get(group).leaveGroup();
				User.removeUserFromGroup(userName, groups.get(group));
				groups.remove(group);
				System.out.println("ok");
			}
		}
	}
	
	private void publish(String group,String message) {
		
		Monitor.write(null, "Handling a publish request for group:"+group+
							" with the message:"+message);
		synchronized (groups) {
			if(!groups.containsKey(group)){
				System.out.println("User not in group " + group.substring(14));
			}else{
				publishMsg(group, userName + Translate.SEPERATOR + lang + Translate.SEPERATOR + message);
			}
		}
	}
	
	private void groupStatus(String groupName) {
		
		Monitor.write(null, "Handling a groupStatuses request");
		synchronized (groups) {
			ChatGroup group = groups.get(groupName);
			if(group==null){
				System.out.println("Not in group " + groupName.substring(14));
			}
			else{
				group.printStatus();
			}
		}
	}
	
	private void myGroups() {
		
		Monitor.write(null, "Handlinng a myGroups request");
		synchronized (groups) {
			for (ChatGroup group : groups.values()) {
				group.printStatus();
			}
		}
	}
	
	private void quit() {
		Monitor.write(null, "Handling a quit request");
		synchronized (groups) {
			for(ChatGroup group : groups.values()){
				group.leaveGroup();
			}
		}
		System.exit(0);
	}
	
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
	private boolean exeCommand(String commandLine) {
		
		// parse and find what command should we execute
		int commandEnding = commandLine.indexOf(" ");
		commandEnding =	(commandEnding == -1)?
						commandLine.length():
						commandEnding;
		// in upper case to make our comparison easier
		String command = commandLine.substring(0, commandEnding).
									 toUpperCase(); 
		String parameter = (commandEnding == commandLine.length())?
							command:
							commandLine.substring(commandEnding+1);
		
		if(command.equals("JOIN")) {
			
			join("dynamicTopics/" + parameter);
			
		} else if(command.equals("LEAVE")) {
			
			leave("dynamicTopics/" + parameter);
			
		} else if(command.equals("PUBLISHMSG")) {
			
			int paramDelim = parameter.indexOf(" ");
			publish("dynamicTopics/" + parameter.substring(0,paramDelim),
					parameter.substring(paramDelim+1));
			
		} else if(command.equals("GROUPSTATUS")) {
			
			groupStatus("dynamicTopics/" + parameter);
			
		} else if(command.equals("MYGROUPS")) {
			
			myGroups();
			
		} else if (command.equals("QUIT")) {
			
			quit();
			return false;
			
		}
		System.out.print(">> ");
		return true;
	}
	
	public static void main(String[] args) throws IOException {
		 
		
		// check that the parameters entered in the correct format
		if( args.length != 2) {
			System.out.println("Please insert parameters in the correct format:\n" +
			"<username> {English | Spanish | French | German}");
			System.exit(0);
		} 
		
		String enteredUsername = args[0];
		// checking that the language is one of {English,Spanish,French,German}
		if(!allowedLang.contains(args[1].toUpperCase())) {
			System.out.println("Please insert a language to be one of the following:\n" +
								"{English,Spanish,French,German}");
			System.exit(0);
		}
		String enteredLang = args[1];
		Monitor.write(null, "Recieved the following parameters:\n" +
							"username:"+enteredUsername+"\n"+
							"langauage:"+enteredLang);
		
		LingvoChat userInstance = new LingvoChat(enteredUsername,enteredLang);
		userInstance.initMessaging();
		
		// waiting for instructions from the user
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		System.out.print(">> ");
		while(userInstance.exeCommand(br.readLine()));
	}
	
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
	
}
